﻿using DeHeng_Mes.Model.BossModel;
using DeHeng_Mes.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using JsonException = System.Text.Json.JsonException;
using DeHeng_Mes.Service.Plc;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Model.Table;
using S7.Net;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Constant;
using JsonSerializer = System.Text.Json.JsonSerializer;
using DeHeng_Mes.Model.Boss;
using System.Linq;
using DeHeng_Mes.Model;
using SqlSugar;
using MappingTable = DeHeng_Mes.Utils.MappingTable;
using DeHeng_Mes.Model.PlcModel;
using ZstdSharp.Unsafe;
using System.Web;
using NPOI.SS.Formula.Functions;
using NPOI.HSSF.Record;
using DeHeng_Mes.Model.Singleton;
using DeHeng_Mes.Model.JsonModel;
using DeHeng_Mes.Model.OrderSingleton;

namespace DeHeng_Mes.Thread
{
    public class FactoryMes
    {


        #region 线程事件变量
        #endregion

        #region 全局成员
        LinePlcToMes plcToMes = new LinePlcToMes();
        OrderDao orderDao = new OrderDao();
        //初始化订单模型
        List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList;
        #endregion


        /// <summary>
        /// 获取工厂Mes下发的订单信息
        /// </summary>
        /// <returns></returns>
        public async Task GetOrderDetails()
        {
            Tools.ProduceLogText("正在开启监听服务...");
            try
            {
                using (HttpListener listener = new HttpListener())
                {
                    // 添加监听地址
                    listener.Prefixes.Add("http://192.168.10.206:7512/");
                    listener.Prefixes.Add("http://10.41.20.37:7512/");

                    // 启动监听
                    listener.Start();
                    Tools.ProduceLogText("监听服务开启成功!");

                    while (true)
                    {
                        // 异步等待客户端请求
                        HttpListenerContext context = await listener.GetContextAsync();

                        // 接收请求，根据请求URL查找对应的服务方法
                        string requestMethod = MappingTable.UrlAnalysis(context.Request.Url.ToString());

                        // 判断请求对应的方法
                        switch (requestMethod)
                        {
                            case "ProcessRequest":
                                _ = Task.Run(() => ProcessRequest(context));
                                break;
                            case "ISVGlue":
                                _ = Task.Run(() => SaveGlueData(context));
                                break;
                            case "ISVWeld":
                                _ = Task.Run(() => SaveWeldData(context));
                                break;
                            default:
                                Tools.ProduceLogTextError($"未知的Url，{context.Request.Url}");
                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError("监听服务开启失败，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 采集弧焊视觉数据
        /// </summary>
        /// <param name="context"></param>
        private async void SaveWeldData(HttpListenerContext context)
        {
            // 初始化响应内容和状态码
            string responseString = string.Empty; // 存储最终的响应内容
            int statusCode = (int)HttpStatusCode.OK; // 默认状态码为200 (OK)

            try
            {
                using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
                {
                    string jsonContent = reader.ReadToEnd();
                    string cleanedString = jsonContent.StartsWith("json=")
                                         ? jsonContent.Substring("json=".Length)
                                         : jsonContent;

                    WeldVObject jsonData = JsonSerializer.Deserialize<WeldVObject>(cleanedString);

                    // 取出检测结果
                    Detailtable[] detailTable = jsonData.DetailTable;

                    int index = 0;

                    List<TraceWeldVision> traceWeldVisions = new List<TraceWeldVision>();

                    for (global::System.Int32 i = 0; i < detailTable.Length; i++)
                    {
                        Detailtable detailtable = detailTable[i];

                        // 创建数据实例
                        TraceWeldVision traceWeldVision = new TraceWeldVision();

                        if (detailTable == null)
                            continue;

                        // 检测项编号+检测类型+结果类型
                        string[] keys = detailtable.key.Split('+');

                        if (keys.Length < 3 || keys[2].Equals("Value"))
                            continue;

                        traceWeldVision.TraceId = jsonData.BarCode;
                        traceWeldVision.No = keys[0];
                        traceWeldVision.Type = keys[1];
                        traceWeldVision.ResultType = keys[2];

                        traceWeldVision.Result = detailtable.value;

                        traceWeldVision.StartTime = DateTime.Parse(jsonData.StartTime);
                        traceWeldVision.EndTime = DateTime.Parse(jsonData.EndTime);
                        traceWeldVision.RobotNo = jsonData.EquipNo;

                        traceWeldVisions.Add(traceWeldVision);

                        index++;
                    }

                    orderDao.WeldVisionDataSave(traceWeldVisions, jsonData.BarCode);

                    Tools.ProduceLogText($"焊缝视觉成功采集到[{index}]条数据，追溯码[{jsonData.BarCode}][{jsonData.StartTime}]");

                    // 返回成功响应
                    responseString = BuildResponseISV();
                }
            }
            catch (JsonException ex)
            {
                // 捕获 JSON 格式解析异常，表示请求数据格式错误，设置状态码为400 (Bad Request)
                responseString = BuildResponseISVNg();
                statusCode = (int)HttpStatusCode.BadRequest;
                Tools.ProduceLogTextError("焊缝检测数据接受失败，Json解析失败：" + ex.Message);
            }
            catch (Exception ex)
            {
                // 捕获所有其他异常，表示服务器内部错误，设置状态码为500 (Internal Server Error)
                responseString = BuildResponse(false, "服务器内部错误");
                statusCode = (int)HttpStatusCode.InternalServerError;
                Tools.ProduceLogTextError("焊缝检测数据接受失败，服务器内部错误：" + ex.Message);
            }
            finally
            {
                // 发送响应数据
                await SendResponseAsync(context, responseString, statusCode);
            }
        }

        /// <summary>
        /// 采集涂胶视觉数据
        /// </summary>
        /// <param name="context"></param>
        private async void SaveGlueData(HttpListenerContext context)
        {
            // 初始化响应内容和状态码
            string responseString = string.Empty; // 存储最终的响应内容
            int statusCode = (int)HttpStatusCode.OK; // 默认状态码为200 (OK)

            try
            {
                using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
                {
                    string jsonContent = reader.ReadToEnd();
                    string cleanedString = jsonContent.StartsWith("json=")
                                         ? jsonContent.Substring("json=".Length)
                                         : jsonContent;

                    RootObject jsonData = JsonSerializer.Deserialize<RootObject>(cleanedString);

                    string code = jsonData.VinCode.Equals("") ? jsonData.StartTime.ToString() : jsonData.VinCode;

                    // 取出检测结果
                    GlueInspectResult[] glueInspectResult = jsonData.GlueInspectResult;

                    List<TraceGlueVision> traceGlueVisions = new List<TraceGlueVision>();

                    foreach (var item in glueInspectResult)
                    {
                        // 创建数据实例
                        TraceGlueVision traceGlueVision = new TraceGlueVision();

                        traceGlueVision.Trace_id = code;

                        // 获取数据
                        traceGlueVision.GlueId = item.GlueId.ToString();
                        traceGlueVision.IsGap = item.IsGap;
                        traceGlueVision.Position = item.Position;
                        traceGlueVision.GlueWidth = item.GlueWidth;
                        traceGlueVision.StartTime = jsonData.StartTime;
                        traceGlueVision.EndTime = jsonData.EndTime;
                        traceGlueVision.Path = jsonData.FtpPath;

                        traceGlueVisions.Add(traceGlueVision);
                    }

                    orderDao.GlueVisionDataSave(traceGlueVisions, code);

                    Tools.ProduceLogText($"存储了[{glueInspectResult.Length}]条涂胶视觉数据，追溯码[{code}]");

                    // 返回成功响应
                    responseString = BuildResponseISV();
                }
            }
            catch (JsonException e)
            {
                BuildResponseISVNg();
                statusCode = (int)HttpStatusCode.BadRequest;

                Tools.ProduceLogTextError("涂胶检测数据解析失败，服务器内部错误：" + e.Message);
            }
            catch (Exception ex)
            {
                // 捕获所有其他异常，表示服务器内部错误，设置状态码为500 (Internal Server Error)
                responseString = BuildResponse(false, "服务器内部错误");
                statusCode = (int)HttpStatusCode.InternalServerError;
                Tools.ProduceLogTextError("涂胶检测数据接受失败，服务器内部错误：" + ex.Message);
            }
            finally
            {
                // 发送响应数据
                await SendResponseAsync(context, responseString, statusCode);
            }
        }

        /// <summary>
        /// 处理接收订单的 HTTP 请求服务
        /// </summary>
        /// <param name="context">HTTP 请求上下文，包含请求与响应信息</param>
        public async Task ProcessRequest(HttpListenerContext context)
        {
            // 初始化响应内容和状态码
            string responseString = string.Empty; // 存储最终的响应内容
            int statusCode = (int)HttpStatusCode.OK; // 默认状态码为200 (OK)

            try
            {
                // 确保请求是 POST 方法
                if (context.Request.HttpMethod != "POST")
                {
                    // 如果请求不是POST方法，则返回未知请求的响应信息
                    responseString = BuildResponse("0", "未知请求");
                    Tools.ProduceLogText($"接收到一个未知的请求方式，请求方式为：{context.Request.HttpMethod}");
                }
                else
                {
                    // 使用 StreamReader 读取请求数据流中的内容
                    using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
                    {
                        // 异步读取请求体内容并转换为字符串
                        string requestBody = await reader.ReadToEndAsync();

                        Tools.ProduceLogText(requestBody);

                        // 将请求体的 JSON 数据反序列化为 OrderPlan 对象
                        OrderPlan jsonData = JsonConvert.DeserializeObject<OrderPlan>(requestBody);

                        // 根据请求进一步获取需要启动的Plc名称下发
                        Tools.ProduceLogText($"接收到一个工单请求，工单号：{jsonData.wo_code}数量：{jsonData.plan_num},生产物料为{jsonData.material_name},请求编码[{jsonData.wo_status}]");

                        switch (jsonData.wo_status)
                        {
                            // 下单
                            case "1":
                                // 生产防错处理
                                responseString = OrderIsError(jsonData);

                                if (!string.IsNullOrEmpty(responseString))
                                {
                                    return;
                                }
                                // 实例化并初始化 LineMesToPlc，用于生成 PLC 订单配置模型
                                var lineToPlc = new LineMesToPlc();

                                DateTime now = DateTime.Now;

                                (string code, string message, string plcName) = lineToPlc.OrderToPlc(jsonData, now);

                                if (code.Equals("1"))
                                {
                                    // 初始化数据库工单数据
                                    bool isSave = InsertOrder(jsonData, plcName, now);

                                    if (!isSave)
                                    {
                                        Tools.ProduceLogTextError("数据库存储工单数据失败，回滚已下发工单！");

                                        code = "0";

                                        message = "工单缓存失败";

                                        OrderEndOrPause(jsonData.material_id, "9", jsonData.wo_code);
                                    }
                                }

                                responseString = BuildResponse(code, message);

                                break;
                            /*// 暂停
                            case "2":
                                responseString = OrderEndOrPause(jsonData.material_id, jsonData.wo_status, jsonData.wo_code, false);
                                break;*/
                            // 报工
                            case "9":
                                responseString = OrderEndOrPause(jsonData.material_id, jsonData.wo_status, jsonData.wo_code);
                                break;
                            default:
                                responseString = BuildResponse("0", "下发失败，未知的状态码");
                                break;
                        }
                    }
                }
            }
            catch (JsonException ex)
            {
                // 捕获 JSON 格式解析异常，表示请求数据格式错误，设置状态码为400 (Bad Request)
                responseString = BuildResponse("0", "请求数据格式错误");
                statusCode = (int)HttpStatusCode.BadRequest;
                Tools.ProduceLogTextError("工厂Mes工单接收失败，原因是：" + ex.Message);
            }
            catch (Exception ex)
            {
                // 捕获所有其他异常，表示服务器内部错误，设置状态码为500 (Internal Server Error)
                responseString = BuildResponse("0", "服务器内部错误");
                statusCode = (int)HttpStatusCode.InternalServerError;
                Tools.ProduceLogTextError("工厂Mes工单接收失败，服务器内部错误：" + ex.Message);
            }
            finally
            {
                // 发送响应数据
                await SendResponseAsync(context, responseString, statusCode);

                Tools.ProduceLogText(responseString);
            }
        }

        /// <summary>
        /// 根据状态码暂停或者结束工单
        /// </summary>
        /// <param name="wo_status"></param>
        /// <returns></returns>
        private string OrderEndOrPause(string material_id, string wo_status, string wo_code)
        {
            string lineStatus = wo_status.Equals("2") ? "暂停" : "结束";

            try
            {
                OrderSingletonModel orderSingletonModel = OrderSingleton.Instance.Where(x => x.OrderId.Equals(wo_code)).FirstOrDefault();

                if (orderSingletonModel == null)
                {
                    return BuildResponse("0", $"当前线体不存在正在生产中的{material_id}");
                }

                if (wo_status.Equals("9"))
                {
                    OrderSingleton.RemoveByOrderId(wo_code);
                }
                else if (wo_status.Equals("2"))
                {
                    // 更新状态
                    OrderSingleton.UpDateStatus(wo_code, wo_status);
                }

                // 更新数据库工单状态
                orderDao.UpdateOrder(wo_code, wo_status);

                return BuildResponse("1", $"{lineStatus}工单成功！");
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{lineStatus}工单失败: {ex.Message}，状态码[{wo_status}]");

                return BuildResponse("0", $"{lineStatus}工单失败");
            }
        }

        /// <summary>
        /// 防错判断
        /// </summary>
        /// <param name="jsonData"></param>
        /// <returns></returns>
        private string OrderIsError(OrderPlan jsonData)
        {
            string responseString = string.Empty;

            // 产线防错判断
            bool isExist = MappingTable.IsExistLine(jsonData.material_id);

            if (!isExist)
                return BuildResponse("0", "下发失败，物料与线体不匹配");

            // 查找数据库是否存在相同工单
            bool status = orderDao.GetOrderStatus(jsonData.wo_code);

            if (status)
            {
                return BuildResponse("0", $"下发失败，历史数据中已存在该工单号");
            }

            bool isSave = OrderSingleton.Instance.Any(x => x.MaterialId.Equals(jsonData.material_id));

            if (isSave)
            {
                return BuildResponse("0", $"下发失败，{jsonData.material_id}当前正在生产!");
            }

            return responseString;
        }

        /// <summary>
        /// 初始化工单数据
        /// </summary>
        /// <param name="lineId"></param>
        /// <param name="orderId"></param>
        /// <param name="produceNum"></param>
        /// <param name="now"></param>
        /// <param name="processCodes"></param>
        private bool InsertOrder(OrderPlan jsonData, string plcName, DateTime now)
        {
            try
            {
                OrderStatus orderStatus = new OrderStatus()
                {
                    LineName = jsonData.eqpt_id,
                    OrderId = jsonData.wo_code,
                    MaterialId = jsonData.material_id,
                    MaterialName = jsonData.material_name,
                    ProductionPlan = jsonData.plan_num.ToString(),
                    Status = jsonData.wo_status,
                    OrderIssuedTime = now,
                    PlcName = plcName,
                };

                int count = orderDao.InsertOrder(orderStatus);

                return count > 0;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 构建响应
        /// </summary>
        /// <param name="success"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private string BuildResponse(bool success, string message) =>
            JsonConvert.SerializeObject(new { Success = success, Message = message });

        private string BuildResponse(string code, string error) =>
            JsonConvert.SerializeObject(new { code = code, error = error });

        private string BuildResponseISV() => "[{\"message\" : \"读取成功\", \"success\":\"true\"}]";
        private string BuildResponseISVNg() => "[{\"message\" : \"格式错误\", \"success\":\"false\"}]";

        private string BuildResponse(bool success, string message, object data) =>
            JsonConvert.SerializeObject(new { Success = success, Message = message, Data = data });

        /// <summary>
        /// 发送响应
        /// </summary>
        /// <param name="context"></param>
        /// <param name="responseString"></param>
        /// <param name="statusCode"></param>
        /// <returns></returns>
        private async Task SendResponseAsync(HttpListenerContext context, string responseString, int statusCode)
        {
            try
            {
                // 将响应字符串转换为 UTF-8 字节数组，以便写入到响应流中
                byte[] buffer = Encoding.UTF8.GetBytes(responseString);

                // 设置响应内容的长度（字节数），ContentLength64 告诉客户端响应体的大小，便于接收完整的响应数据
                context.Response.ContentLength64 = buffer.Length;

                // 设置响应的内容类型为 JSON，告知客户端响应的数据格式
                context.Response.ContentType = "application/json";

                // 设置响应的 HTTP 状态码，指示请求的处理结果状态
                context.Response.StatusCode = statusCode;

                // 异步写入响应内容到输出流，将字节数组内容从起始位置写到响应流中
                await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);

                // 关闭响应输出流，表示响应已经完成，释放资源
                context.Response.OutputStream.Close();
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"发送响应失败，原因是：{ex.Message}");
            }
        }


        /// <summary>
        /// 开局防错判断
        /// </summary>
        public async Task OrderIsOk()
        {
            // 日志输出标识
            bool isOut = true;

            // 初始化Plc连接缓存
            List<string> plcConfigModels = new List<string>();

            // 检查连接状态并查询工单
            foreach (var item in PlcConnectionManager.plcContainer)
            {
                if (item.isConnected)
                {
                    QueryOrderInPlc(item);
                }
                else
                {
                    plcConfigModels.Add(item.name);
                }
            }

            await Task.Delay(3000);

            OrderConfig();

            // 循环检查未连接的PLC
            while (plcConfigModels != null && plcConfigModels.Any())
            {
                // 再次检查未连接的PLC
                for (int i = plcConfigModels.Count - 1; i >= 0; i--)
                {
                    var itemName = plcConfigModels[i];
                    var plc = PlcConnectionManager.plcContainer.FirstOrDefault(p => p.name == itemName);

                    if (plc == null || !plc.isConnected)
                    {
                        if (plc != null && isOut)
                            Tools.ProduceLogTextError($"{plc.nick}未连接，无法配置采集线程");  // 记录日志但不重复添加到缓存
                    }
                    else
                    {
                        // 查询是否存在订单数据，如果有订单数据那么重新配置订单模型，加入缓存
                        QueryOrderInPlc(plc);

                        plcConfigModels.RemoveAt(i);
                    }
                }
                isOut = false;

                await Task.Delay(1000);
            }
        }

        /// <summary>
        /// 配置工单
        /// </summary>
        public void OrderConfig()
        {
            try
            {
                List<OrderStatus> orderStatuses = orderDao.QueryOrder();

                if (orderStatuses?.Count > 0)
                {
                    foreach (var orderStatus in orderStatuses)
                    {
                        if (orderStatus.PlcName.Contains(","))
                        {
                            foreach (string item in orderStatus.PlcName.Split(','))
                            {
                                OrderAdd(orderStatus, item);
                            }
                        }
                        else
                        {
                            OrderAdd(orderStatus, orderStatus.PlcName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"配置工单失败，原因是{ex.Message}");
            }
        }

        private void OrderAdd(OrderStatus orderStatus, string item)
        {
            OrderSingleton.AddOrder(new OrderSingletonModel()
            {
                LineName = orderStatus.LineName,
                MaterialId = orderStatus.MaterialId,
                MaterialName = orderStatus.MaterialName,
                OrderId = orderStatus.OrderId,
                Status = orderStatus.Status,
                ProducessNum = int.Parse(orderStatus.ProductionPlan),
                ProducessNumNow = orderStatus.EndProduction,
                PlcName = item,
                OrderCreateTime = orderStatus.OrderIssuedTime
            });

            Tools.ProduceLogText($"成功配置了一个工单，工单号为[{orderStatus.OrderId}]，当前存在[{OrderSingleton.GetCount()}]个工单缓存");
        }

        /// <summary>
        /// 重新配置工单模型
        /// </summary>
        /// <param name="plcConfigModel"></param>
        private void QueryOrderInPlc(PlcConfigModel plcConfigModel)
        {
            try
            {
                PlcOrderConfigModel plcOrderConfigModel = new PlcOrderConfigModel()
                {
                    plc = plcConfigModel.plc,
                    PlcName = plcConfigModel.name,
                    Status = false,
                };

                (byte[] bytes, List<string> stationList) = Tools.GetStatusBytes(plcOrderConfigModel);

                if (bytes == null || stationList == null)
                    return;

                int index = 0;

                foreach (string station in stationList)
                {
                    // 获取新状态值
                    plcOrderConfigModel.stationStatus[station] = S7.Net.Types.Int.FromByteArray(bytes.Skip(index * 50 + 2).Take(2).ToArray());

                    index++;
                }

                readPlcList.Add(plcOrderConfigModel);

                Tools.ProduceLogText($"{plcConfigModel.name}成功配置一个采集线程");
            }
            catch (Exception ex)
            {
                Tools.ProduceLogText($"配置读取线程失败，原因是{ex.Message}");
            }
        }
    }
}